﻿#pragma once

#include <pcl/filters/filter_indices.h>

using namespace pcl;
namespace oil
{
/**\brief 采用改进的morphological filter剔除机载LIDAR点云中的粗差
*
*基本思想是，对点云先进行格网划分得到A，A中每个格网只记录最低点，只有最低点认为是low outlier。
*然后，得到(2*mophological_size_+1) * (2*mophological_size_+1)大小窗口A中的值，并且排序。
*认为最多这些格网中只会有max_low_grids_个网格会包含low outlier，那么创建一个数组Zf记录排序后的第max_low_grids_。
*之所以这么做，而不是直接记录最大值，是因为此时的目的仅仅是为了滤掉low outlier，如果记录最大值的话，那么当地面上的点不是成片的话，后面就回不来了。
*这时，可以认为已经不包含low outlioer的格网了，最后一部和morphological filter一样，再做一个闭运算，得到变换窗口中的最小值Z
*最后判断点P 和 Z 之间的大小关系，如果P - Z < -low_threshold_ 那么就是low outlier。 
*如果设置negtive 为 true的话，则得到的是low outlier；否则得到的indices 是非low outlier；默认值为false
*
*/
template < typename PointT >
class LowOutlierFilter : public FilterIndices < PointT >
{
protected:
	typedef typename FilterIndices<PointT>::PointCloud PointCloud;
	typedef typename PointCloud::Ptr PointCloudPtr;
	typedef typename PointCloud::ConstPtr PointCloudConstPtr;

public:
	typedef boost::shared_ptr< LowOutlierFilter<PointT> > Ptr;
	typedef boost::shared_ptr< const LowOutlierFilter<PointT> > ConstPtr;

	LowOutlierFilter(){
		window_size_ = 2.0;
		low_threshold_ = 0.5; 
		mophological_size_ = 2;
		max_low_grids_ = 6;
	}
	double getWindowSize() const { return window_size_; }
	void setWindowSize(double val) { window_size_ = val; }
	
	double getLowThreshold() const { return low_threshold_; }
	void setLowThreshold(double val) { low_threshold_ = val; }

	int getMaxLowGrids() const { return max_low_grids_; }
	void setMaxLowGrids(int val) { max_low_grids_ = val; }

	int getMophologicalSize() const { return mophological_size_; }
	void setMophologicalSize(int val) { mophological_size_ = val; }

protected:
	using PCLBase<PointT>::input_;
	using PCLBase<PointT>::indices_;
	using Filter<PointT>::filter_name_;
	using Filter<PointT>::getClassName;
	using FilterIndices<PointT>::negative_;
	using FilterIndices<PointT>::keep_organized_;
	using FilterIndices<PointT>::user_filter_value_;
	using FilterIndices<PointT>::extract_removed_indices_;
	using FilterIndices<PointT>::removed_indices_;

	/**\brief 格网窗口大小，通常2-5米即可。
	*
	*这个窗口不宜太小，一个是计算时间太长，再一个是邻域内窗口的low outlier可能会变多。
	*也不宜太大，太大的话，在地形起伏剧烈的的确效果也不会太好。
	*/	
	double window_size_;

	/**\brief 判断小于地面高度多少，即为low outlier，这个现在被中值替代掉
	*/
	double low_threshold_;
	
	/**\brief 形态学变换大小，影响范围为(2*size+1)*(2*size+1)
	*
	*/
	int mophological_size_;
	
	/**\brief 在变换窗口中，最多会有多少个low outlier
	*
	*在进行开运算的时候，并不是记录最高值，因为这样需要有成片的地面区域，在开运算后扔能保留地面点。
	*因此这里的策略是，只考虑最多有多少个格网会是low outlier，然后开运算取值，就只取比他多一个。
	*那么这里就肯定不会是low outlier的格网了。
	*/
	int max_low_grids_;
	

	/*************************************************************************/
	/**
	* \brief		重载函数，只返回索引 	
	*/ 
	/*************************************************************************/
	void applyFilter(std::vector<int> &indices)
	{
		applyFilterIndices(indices);
	}

	void applyFilterIndices(std::vector<int> &indices);

	/*************************************************************************/
	/**
	* \brief		重载函数，返回点云 
	*/ 
	/*************************************************************************/
	void applyFilter(PointCloud &output);
};

}
#ifdef PCL_NO_PRECOMPILE
#include <oil/oil_classification/low_outlier_filter.hpp>
#endif